// Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
//
// Licensed under the Mulan PSL v2.
// You can use this file according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of the License at
//
//     http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
// FOR MORE DETAILS.

#include "DBManager/Repository/HDF5/Input/Property/PBEAMLRepository.h"

#include "DBManager/IRepository/Input/Property/IPropertySummaryRepository.h"
#include "DBManager/Repository/HDF5/Base/HDF5RepositoryMacro.h"

HDF5_REG_REPOSITORY (PBEAMLRepository)

using namespace SG::DBManager::HDF5;

PBEAMLRepository::PBEAMLRepository (HDF5Database& database) : MainlyRepositoryBase<SG::DataStructure::FEM::PBEAMLData> (database)
{}

void PBEAMLRepository::InitialDatasetInfo ()
{
    RegisterIndex ("/OWN/INDEX/INPUT/PROPERTY/PBEAML", IndexDataset, GetType ());
    RegisterTableInfo<PBEAML::IDENTITY> ("/SG/INPUT/PROPERTY/PBEAML/IDENTITY", MainDataset);
    RegisterTableInfo<PBEAML::SECTION> ("/SG//INPUT/PROPERTY/PBEAML/SECTION", SECTION);
    RegisterTableInfo<SG::DataStructure::Common::Real> ("/SG//INPUT/PROPERTY/PBEAML/DIMS", DIMS);

    // 连接信号槽 当调用汇总的时候，将已有的缓存信息全部都持久化到HDF5文件中。
    GetDatabase ().GetHDF5Hub()->m_propertySummarySignal.connect ([this] { Sort (); });
}

void PBEAMLRepository::DumpWriteCahce ()
{
    auto& writeStorage = this->GetMainCache ().m_writeStorage;

    // 设置为无顺序
    GetMainDatasetInfo ().SetIsSequential (false);

    std::vector<PBEAML::IDENTITY>                IDENTITYData;
    std::vector<PBEAML::SECTION>                 SECTIONData;
    std::vector<SG::DataStructure::Common::Real> DIMSData;

    OwnToNastran (writeStorage, IDENTITYData, SECTIONData, DIMSData);

    WriteData (MainDataset, IDENTITYData);
    WriteData (SECTION, SECTIONData);
    WriteData (DIMS, DIMSData);

    // 更新Index表
    WriteIndexData (this->GetIndexDatasetInfo (), writeStorage);

    writeStorage.clear ();
}

std::vector<PBEAMLRepository::DataType>*  PBEAMLRepository::LoadReadCache (std::size_t index)
{
    std::vector<PBEAML::IDENTITY> IDENTITYData;
    LoadMainMetadata (index, IDENTITYData);

    if (IDENTITYData.empty ())
        return nullptr;

    // 读取范围内的附表信息
    std::vector<PBEAML::SECTION> SECTIONData;
    LoadSecondaryMetadata (IDENTITYData, SECTION_POS, SECTION_LEN, SECTION, SECTIONData);

    // 读取范围内的附表信息
    std::vector<SG::DataStructure::Common::Real> DIMSData;
    LoadSecondaryMetadata (SECTIONData, DIMS_POS, DIMS_LEN, DIMS, DIMSData);

    auto* cacheVector = m_cacheHelper.LookupOrCreateVector<DataType> (GetMainCache ().m_readCacheKey, IDENTITYData.size ());
    NastranToOwn (IDENTITYData, SECTIONData, DIMSData, *cacheVector);
    return cacheVector;
}

void PBEAMLRepository::SortData ()
{
    // 获取所有数据 👇
    std::vector<PBEAML::IDENTITY> IDENTITYData;
    GetDataset (MainDataset).read (IDENTITYData);

    if (IDENTITYData.empty ())
    {
        GetMainDatasetInfo ().SetIsSequential (true);
        return;
    }

    std::vector<PBEAML::SECTION> SECTIONData;
    GetDataset (SECTION).read (SECTIONData);

    std::vector<SG::DataStructure::Common::Real> DIMSData;
    GetDataset (DIMS).read (DIMSData);

    // 将所有数据转换
    std::vector<SG::DataStructure::FEM::PBEAMLData> AllData (IDENTITYData.size ());
    NastranToOwn (IDENTITYData, SECTIONData, DIMSData, AllData);

    // 排序
    std::sort (AllData.begin (), AllData.end ());

    // 转换回 Nastran格式
    IDENTITYData.clear ();
    SECTIONData.clear ();
    DIMSData.clear ();
    OwnToNastran (AllData, IDENTITYData, SECTIONData, DIMSData, true);

    // 写出数据至文件
    GetDataset (MainDataset).write (IDENTITYData);
    GetDataset (SECTION).write (SECTIONData);
    GetDataset (DIMS).write (DIMSData);

    // 重写索引
    RewriteIndexData (AllData);

    // 设置为已顺序
    GetMainDatasetInfo ().SetIsSequential (true);
}

std::vector<SG::DataStructure::FEM::PBEAMLData> PBEAMLRepository::GetAllData ()
{
    Sort ();

    std::vector<SG::DataStructure::FEM::PBEAMLData> result;

    // 获取所有数据 👇
    std::vector<PBEAML::IDENTITY> IDENTITYData;
    GetDataset (MainDataset).read (IDENTITYData);

    if (IDENTITYData.empty ())
    {
        return result;
    }

    std::vector<PBEAML::SECTION> SECTIONData;
    GetDataset (SECTION).read (SECTIONData);

    std::vector<SG::DataStructure::Common::Real> DIMSData;
    GetDataset (DIMS).read (DIMSData);

    // 将所有数据转换
    NastranToOwn (IDENTITYData, SECTIONData, DIMSData, result);

    return result;
}

void PBEAMLRepository::OwnToNastran (std::vector<SG::DataStructure::FEM::PBEAMLData>&   own,
                                     std::vector<PBEAML::IDENTITY>& _OUT                IDENTITYData,
                                     std::vector<PBEAML::SECTION>& _OUT                 SECTIONData,
                                     std::vector<SG::DataStructure::Common::Real>& _OUT DIMSData,
                                     bool                                               isSort)
{
    IDENTITYData.clear ();
    SECTIONData.clear ();
    DIMSData.clear ();
    IDENTITYData.reserve (own.size ());

    // 获取已有数据条数，主要是放在主表的POS字段
    // 如果是重新排序的时候，Pos都是从0 开始的
    size_t DIMSCount    = isSort ? 0 : this->GetDatasetInfo (DIMS).GetRowCount ();
    size_t SECTIONCount = isSort ? 0 : this->GetDatasetInfo (SECTION).GetRowCount ();

    // N开头的为 Nastran的数据结构 O开头的是自己的
    for (auto&& OwnIdent : own)
    {
        for (auto&& Osection : OwnIdent.m_section)
        {
            // 先写DIMS  获取DIMS的POS  与LENS 放在SECTION中
            DIMSData.insert (DIMSData.end (), Osection.m_dims.begin (), Osection.m_dims.end ());

            // 写入SECTION
            PBEAML::SECTION Nsection{};
            Nsection.SO       = Osection.m_so;
            Nsection.RDIST    = Osection.m_rdist;
            Nsection.NSM      = Osection.m_nsm;
            Nsection.DIMS_POS = static_cast<SG::DataStructure::Common::I64> (DIMSCount);
            Nsection.DIMS_LEN = static_cast<SG::DataStructure::Common::I64> (Osection.m_dims.size ());
            SECTIONData.emplace_back (Nsection);

            DIMSCount += Osection.m_dims.size ();
        }
        // 写入IDENTITY
        PBEAML::IDENTITY NasIdent{};
        NasIdent.PID = OwnIdent.m_id;
        NasIdent.MID = OwnIdent.m_mId;
        std::memcpy (NasIdent.GROUP, OwnIdent.m_group, 8);
        std::memcpy (NasIdent.TYPE, OwnIdent.m_type, 8);
        NasIdent.SECTION_POS = static_cast<SG::DataStructure::Common::I64> (SECTIONCount);
        NasIdent.SECTION_LEN = static_cast<SG::DataStructure::Common::I64> (OwnIdent.m_section.size ());
        NasIdent.DOMAIN_ID   = OwnIdent.m_domainId;

        IDENTITYData.emplace_back (NasIdent);

        SECTIONCount += OwnIdent.m_section.size ();
    }
}

void PBEAMLRepository::NastranToOwn (std::vector<PBEAML::IDENTITY>&                        IDENTITYData,
                                     std::vector<PBEAML::SECTION>&                         SECTIONData,
                                     std::vector<SG::DataStructure::Common::Real>&         DIMSData,
                                     std::vector<SG::DataStructure::FEM::PBEAMLData>& _OUT own)
{
    own.clear ();
    own.reserve (IDENTITYData.size ());

    // 代表初始的Index 位置, 因为数据有可能是截取了附表的一部分，所有主表中根据POS找的时候需要对应一下，
    // 比如第一个主表数据中POS字段为10000， 附表的vector的数据 就是从附表中的第10000个开始对应的 也就是Index==0的
    const SG::DataStructure::Common::I64 sIndex = IDENTITYData[0].SECTION_POS;
    const SG::DataStructure::Common::I64 dIndex = SECTIONData[0].DIMS_POS;

    for (auto&& NasIdent : IDENTITYData)
    {
        // 创建主表
        SG::DataStructure::FEM::PBEAMLData OwnIdent;
        OwnIdent.m_id  = NasIdent.PID;
        OwnIdent.m_mId = NasIdent.MID;
        memcpy (OwnIdent.m_group, NasIdent.GROUP, 8);
        memcpy (OwnIdent.m_type, NasIdent.TYPE, 8);
        OwnIdent.m_domainId = NasIdent.DOMAIN_ID;

        // 创建附表
        for (SG::DataStructure::Common::I64 i = 0; i < NasIdent.SECTION_LEN; ++i)
        {
            SG::DataStructure::FEM::PBEAMLData::section Osection;
            PBEAML::SECTION const                       Nsection = SECTIONData[NasIdent.SECTION_POS - sIndex + i];

            Osection.m_so    = Nsection.SO;
            Osection.m_rdist = Nsection.RDIST;
            Osection.m_nsm   = Nsection.NSM;
            Osection.m_dims.assign (DIMSData.begin () + Nsection.DIMS_POS - dIndex,
                                    DIMSData.begin () + Nsection.DIMS_POS + Nsection.DIMS_LEN - dIndex);

            OwnIdent.m_section.emplace_back (Osection);
        }

        own.emplace_back (OwnIdent);
    }
}
